#include <Box2D/Box2D.h>
#include <QyDebug.h>
#include "QpwDecorationObject.h"
#include "QpwGeometry.h"
#include "QpwPhysics.h"

using namespace QpwPhysics;

/**************************************************************************************************/

inline b2Vec2 * geometryToVecArray( const QpwWorld::Geometry & geo, const QPointF & center ) {

    if ( geo.isEmpty() )
        return 0;

    b2Vec2 * vecs = new b2Vec2[ geo.count() ];
    quint32 i = 0;

    foreach ( QPointF point, geo ) {

        point += center;
        vecs[i].x = point.x();
        vecs[i].y = point.y();
        ++i;

    }

    return vecs;
}

inline b2Shape * createShape( const QpwWorld::Geometry & geo ) {

//     QyDbgValue( geo.isCircle() );
    if ( geo.isCircle() ) {

        b2CircleShape * shape = new b2CircleShape;

        shape->m_radius = geo.radius();
        shape->m_p.x = geo.center.x();
        shape->m_p.y = geo.center.y();

//         qDebug( "Physics: Created circle shape %f", shape->m_radius );

        return shape;
    } else {

        b2Vec2 * vecs = geometryToVecArray( geo, geo.center );

        if (!vecs) {

            qWarning("Physics: Can't create body shape for invalid polygon");

            return 0;
        }

        b2PolygonShape * shape = new b2PolygonShape;

//         qDebug( "Physics: Created polygon shape %d", geo.count() );
        shape->Set( vecs, geo.count() );
//         delete [] vecs;

        return shape;
    }

    return 0;
}

/**************************************************************************************************/

/*static */ObjectData * ObjectData::fromGeometry( b2World * world,
    const QpwWorld::Geometry & geometry, QpwWorld::DecorationObject::ObjectTypes type )
{

    b2Body * body;
    b2BodyDef bodyDef;
    b2Shape * shape;
    b2FixtureDef fixtureDef;

    //
    qreal density = 1.0;
    qreal friction = 1.0;
    qreal linearDamping = 1.0;
    qreal angularDamping = 1.0;
    //

    switch (type) {

    case QpwWorld::DecorationObject::DecorationObjectType:
        return 0;

    case QpwWorld::DecorationObject::StaticObjectType:
    case QpwWorld::DecorationObject::QuasiStaticObjectType:

        if ( geometry.subGeometry.count() ) {

            QpwWorld::Geometry mainGeometry = geometry.subGeometry.first();

            bodyDef.position.Set( mainGeometry.center.x(), mainGeometry.center.y() );
            body = world->CreateBody(&bodyDef);

            foreach ( QpwWorld::Geometry subGeo, geometry.subGeometry ) {

                shape = createShape(subGeo);
                body->CreateFixture( shape, density );
                delete shape;

            }

        } else {

            bodyDef.position.Set( geometry.center.x(), geometry.center.y() );
            body = world->CreateBody(&bodyDef);
            shape = createShape(geometry);
            body->CreateFixture( shape, density );
            delete shape;

        }

        return new StaticObjectData(body);

    case QpwWorld::DecorationObject::DynamicObjectType:

        bodyDef.type = b2_dynamicBody;
        bodyDef.linearDamping = linearDamping;
        bodyDef.angularDamping = angularDamping;
        bodyDef.position.Set( geometry.center.x(), geometry.center.y() );

        if ( geometry.subGeometry.count() ) {

            QpwWorld::Geometry mainGeometry = geometry.subGeometry.first();
            body = world->CreateBody(&bodyDef);

            foreach ( QpwWorld::Geometry subGeo, geometry.subGeometry ) {

                fixtureDef.shape = createShape(subGeo);

                if (fixtureDef.shape) {

                    fixtureDef.density = density;
                    fixtureDef.friction = friction;
                    body->CreateFixture(&fixtureDef);
                    delete fixtureDef.shape;

                }

            }

        } else {

            body = world->CreateBody(&bodyDef);
            fixtureDef.shape = createShape(geometry);

            if (fixtureDef.shape) {

                fixtureDef.density = density;
                fixtureDef.friction = friction;
                body->CreateFixture(&fixtureDef);
                delete fixtureDef.shape;

            }

        }

        return new DynamicObjectData(body);
    }

    return 0;
}

/*static */ObjectData * ObjectData::fromSubType( b2World * world, const QpwSubType & subType,
    const QpwGeometryDatabase & geometryDatabase )
{

    b2Body * body;
    b2BodyDef bodyDef;
    b2Shape * shape;
    b2FixtureDef fixtureDef;
    QpwWorld::Geometry geometry;

    if ( geometryDatabase.rows.contains(subType.geometryUuid) )
        geometry = geometryDatabase.rows[subType.geometryUuid];

//     QyDbgValue( subType.geometryUuid );
//     QyDbgValue( geometry.uuid );
//     QyDbgValue( geometry.count() );

    switch (subType.baseType) {

    case QpwWorld::DecorationObject::DecorationObjectType:
        return 0;

    case QpwWorld::DecorationObject::StaticObjectType:
    case QpwWorld::DecorationObject::QuasiStaticObjectType:

        if ( geometry.subGeometry.count() ) {

            QpwWorld::Geometry mainGeometry = geometry.subGeometry.first();

            bodyDef.position.Set( mainGeometry.center.x(), mainGeometry.center.y() );
            body = world->CreateBody(&bodyDef);

            foreach ( QpwWorld::Geometry subGeo, geometry.subGeometry ) {

                shape = createShape(subGeo);

                if (shape) {

                    body->CreateFixture( shape, subType.density );
                    delete shape;

                }

            }

        } else {

            bodyDef.position.Set( geometry.center.x(), geometry.center.y() );
            body = world->CreateBody(&bodyDef);
            shape = createShape(geometry);

            if (shape) {

                body->CreateFixture( shape, subType.density );
                delete shape;

            } else {

                world->DestroyBody(body);
                body = 0;

            }

        }

        return body ? new StaticObjectData(body) : 0;

    case QpwWorld::DecorationObject::DynamicObjectType:

        bodyDef.type = b2_dynamicBody;
        bodyDef.linearDamping = subType.linearDamping;
        bodyDef.angularDamping = subType.angularDamping;

        if ( geometry.subGeometry.count() ) {

            QpwWorld::Geometry mainGeometry = geometry.subGeometry.first();

            bodyDef.position.Set( mainGeometry.center.x(), mainGeometry.center.y() );
            body = world->CreateBody(&bodyDef);

            foreach ( QpwWorld::Geometry subGeo, geometry.subGeometry ) {

                fixtureDef.shape = createShape(subGeo);

                if (fixtureDef.shape) {

                    fixtureDef.density = subType.density;
                    fixtureDef.friction = subType.friction;
                    body->CreateFixture(&fixtureDef);
                    delete fixtureDef.shape;

                }

            }

        } else {

            bodyDef.position.Set( geometry.center.x(), geometry.center.y() );
            body = world->CreateBody(&bodyDef);
            fixtureDef.shape = createShape(geometry);

            if (fixtureDef.shape) {

                fixtureDef.density = subType.density;
                fixtureDef.friction = subType.friction;
                body->CreateFixture(&fixtureDef);
                delete fixtureDef.shape;

            } else {

                world->DestroyBody(body);
                body = 0;

            }

        }

        return body ? new DynamicObjectData(body) : 0;
    }

    return 0;
}
